﻿using System;
using System.Reflection;
using System.Windows.Forms;
using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using Argos.Activities.Tests;
using Argos.Workflows;
using System.Collections.Generic;
using System.ComponentModel;
using Argos.Collections;

namespace Argos.Windows.Forms
{
	public partial class TypeSelector: UserControl
	{

		#region #ctor: public TypeSelector()

		/// <summary>
		/// Creates a new instance of the class.
		/// </summary>
		public TypeSelector()
		{
			InitializeComponent();
			this.TypeMatcher = Predicates.DefaultTypeMatcher;
		}

		#endregion
		
		#region #ctor: public TypeSelector(Predicate<Type> matcher)

		/// <summary>
		/// Creates a new instance of the class and specifies the type matcher to use.
		/// </summary>
		/// <param name="matcher">The matcher that matches the types displayed in the selector.</param>
		public TypeSelector(Predicate<Type> matcher) : this()
		{
			if (null == matcher)
			{
				throw new ArgumentNullException("matcher");
			}
			this.TypeMatcher = matcher;
		}

		#endregion
		
		
		
		#region public event EventHandler<EventArgs> TypesSelected

		private EventHandler<EventArgs> TypesSelectedHandler;

		/// <summary>
		/// Fired when the user uses the keyboard to select one or many types
		/// in the selector.
		/// </summary>
		public event EventHandler<EventArgs> TypesSelected
		{
			add { this.TypesSelectedHandler += value; }
			remove { this.TypesSelectedHandler -= value; }
		}

		#endregion



		#region public bool MultiSelect { get; set; }

		[DefaultValue(false)]
		[Description("Specifies whether to allow users to select more than one type in the selector.")]
		public bool MultiSelect
		{
			get { return this.TypeGrid.MultiSelect; }
			set
			{
				if (!bool.Equals(this.MultiSelect, value))
				{
					this.TypeGrid.MultiSelect = value;
					this.PopulateTypeList();
				}
			}
		}

		#endregion

		#region public IList<Type> SelectedTypes { get; }

		private EventfulList<Type> _SelectedTypes;

		[Browsable(false)]
		[DefaultValue(null)]
		public IList<Type> SelectedTypes
		{
			get
			{
				if (null == _SelectedTypes)
				{
					_SelectedTypes = new EventfulList<Type>();
				}
				return _SelectedTypes;
			}
		}

		#endregion

		#region public Predicate<Type> TypeMatcher { get; set; }

		private Predicate<Type> _TypeMatcher;

		[Browsable(false)]
		public Predicate<Type> TypeMatcher
		{
			get { return _TypeMatcher; }
			set
			{
				if (null == value)
				{
					throw new ArgumentNullException("TypeMatcher", "The TypeMatcher property cannot be set to null. Use one of the static properties defined in the class if you don't want to specify your own type matcher.");
				}

				if (!object.Equals(this.TypeMatcher, value))
				{
					_TypeMatcher = value;
					this.PopulateTypeList();
				}
			}
		}

		#endregion



		protected virtual void OnTypesSelected(EventArgs e)
		{
			if (null != this.TypesSelectedHandler)
			{
				this.TypesSelectedHandler.Invoke(this, e);
			}
		}



		private void AddTypeToList(Type type)
		{
			string description = type.GetDescription();
			string name = !string.IsNullOrEmpty(type.GetDisplayName()) ? type.GetDisplayName() : type.Name;

			int index = this.TypeGrid.Rows.Add(name, description, type.FullName, type.Assembly.FullName);
			this.TypeGrid.Rows[index].Tag = type;

			if (this.SelectedTypes.Contains(type))
			{
				this.TypeGrid.Rows[index].Selected = true;
			}
		}

		private void PopulateTypeList()
		{
			this.TypeGrid.Rows.Clear();

			if (object.Equals(this.TypeMatcher, Predicates.DefaultTypeMatcher))
			{
				return;
			}

			Assembly[] asmList = AppDomain.CurrentDomain.GetAssemblies();

			foreach (Assembly asm in asmList)
			{
				foreach (Type t in asm.GetTypes())
				{
					if (this.TypeMatcher.Invoke(t))
					{
						this.AddTypeToList(t);
					}
				}
			}

			this.TypeGrid.Sort(this.FullTypeNameColumn, ListSortDirection.Ascending);
		}

		private void TypeGrid_SelectionChanged(object sender, EventArgs e)
		{
			this.SelectedTypes.Clear();
			
			foreach (DataGridViewRow row in this.TypeGrid.SelectedRows)
			{
				this.SelectedTypes.Add((Type)row.Tag);
			}
		}

		private void TypeGrid_DoubleClick(object sender, EventArgs e)
		{

			if (e is MouseEventArgs)
			{
				MouseEventArgs em = (MouseEventArgs)e;
				if (this.TypeGrid.HitTest(em.X, em.Y).RowIndex >= 0)
				{
					this.OnTypesSelected(EventArgs.Empty);
				}

				return;
			}

			this.OnTypesSelected(EventArgs.Empty);
		}

		private void TypeGrid_KeyDown(object sender, KeyEventArgs e)
		{
			if (e.Modifiers == Keys.None)
			{
				if (e.KeyCode == Keys.Enter && this.TypeGrid.SelectedRows.Count > 0)
				{
					this.OnTypesSelected(EventArgs.Empty);
					e.Handled = true;
					e.SuppressKeyPress = true;
				}
			}
		}

	}
}
